跳到主要内容

MySQL 学习(4) Master Thread 的工作

TODO: 这篇文章,由于我的知识水平有限,用到的时候再更新...

Master Thread 是干什么的?

Master thread 是核心的后台线程,主要负责将缓冲池中的数据异步刷新到磁盘,保证数据的一致性,包括脏页的刷新、合并插入缓冲、undo 页的回收等。

InnoDB 1.0X 版本之前的 Master Thread

Master Thread 具有最高的线程优先级别,内部由多个循环组成:主循环(loop)、后台循环(background loop)、刷新循环(flush loop)、暂停循环(suspend loop),Master Thread 会根据数据库运行的状态进行循环之间的切换。

Loop主循环(大多数操作都在这个循环下)这个由两大部分操作,每秒和每10秒操作:

void master_thread() {
loop:
for(int i=0; i<10; i++) {
do thing once per second
sleep 1 second if necessary
}
do things once per then seconds
goto loop;
}

可以发现,loop 循环是通过 thread sleep 来实现的,意味着每秒或者每 10 每秒的操作并不是十分的精确的,在负载大的情况下,可能会有不同程度的延迟(delay)。

每秒一次的操作

1、日志缓冲刷新到磁盘 即使这个事务还没有提交(总是),这点解释了为什么再大的事务 commit 时都很快;

2、合并插入缓冲(可能) 合并插入并不是每秒都发生,InnoDB 会判断当前一秒内发生的 IO 次数是否小于 5,如果是,则系统认为当前的 IO 压力很小,可以执行合并插入缓冲的操作。

3、至多刷新 100 个 InnoDB 的缓冲池的脏页到磁盘(可能) 这个刷新 100 个脏页也不是每秒都在做,InnoDB 引擎通过判断当前缓冲池中脏页的比例(buf_get_modified_ratio_pct)是否超过了配置文件中 innodb_max_dirty_pages_pct 参数(默认是 90,即 90%),如果超过了这个阈值,InnoDB 引擎认为需要做磁盘同步操作,将 100 个脏页写入磁盘。

基于上面的操作,伪代码可以进一步具体化,如下所示:

void master_thread() {
goto loop;
loop:
for(int i = 0; i<10; i++) {
thread_sleep(1) // sleep 1 second
do log buffer flush to disk
if (last_one_second_ios < 5 )
do merge at most 5 insert buffer
if ( buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct )
do buffer pool flush 100 dirty page
if ( no user activity )
goto background loop
do things once per ten seconds
background loop :
do something
goto loop:
}

每 10 秒一次的操作

1、刷新 100 个脏页到磁盘(可能) InnoDB 引擎先判断过去 10 秒内磁盘的 IO 操作是否小于 200 次,如果是,认为当前磁盘有足够的 IO 操作能力,即将 100 个脏页刷新到磁盘。

2、合并至多 5 个插入缓冲(总是) 此次的合并插入缓冲操作总会执行,不同于每秒操作时可能发生的合并操作。

3、将日志缓冲刷新到磁盘(总是) InnoDB 引擎会再次执行日志缓冲刷新到磁盘的操作,与每秒发生的操作一样。

4、删除无用的 undo 页(总是) 当对表执行 update、delete 操作时,原先的行会被标记为删除,但是为了一致性读的关系,需保留这些行版本的信息,在进行 10S 一次的删除操作时,InnoDB 引擎会判断当前事务系统中已被删除的行是否可以删除,如果可以,InnoDB 会立即将其删除。InnoDB 每次最多删除 20 个 Undo 页。(使用 full purge 操作)

5、刷新 100 个或者 10 个脏页到磁盘(总是) 然后,InnoDB 存储引擎会判断缓冲池中脏页的比例(buf_get_modified_ratio_pct),如果有超过 70% 的脏页,则刷新 100 个脏页到磁盘,如果脏页的比例小于 70%,则只需刷新 10% 的脏页到磁盘。

现在我们可以完整地把主循环(main loop)的伪代码写出来了,内容如下:

void master_thread(){
goto loop;
loop:
for(int i = 0; i<10; i++){
thread_sleep(1) // sleep 1 second
do log buffer flush to disk
if (last_one_second_ios < 5 )
do merge at most 5 insert buffer
if ( buf_get_modified_ratio_pct > innodb_max_dirty_pages_pct )
do buffer pool flush 100 dirty page
if ( no user activity )
goto background loop
}

if ( last_ten_second_ios < 200 )
do buffer pool flush 100 dirty page
do merge at most 5 insert buffer
do log buffer flush to disk
do full purge
if ( buf_get_modified_ratio_pct > 70% )
do buffer pool flush 100 dirty page
else
buffer pool flush 10 dirty page
goto loop
background loop:
do something
goto loop:
}

background loop 操作

接着来看 background loop 若当前没有用户活动(数据库空闲时)或者数据库关闭(shutdown),就会切换到这个循环执行以下操作:

1、删除无用的undo页(总是)

2、合并20个插入缓冲(总是)

3、跳回到主循环(总是)

4、不断,直到符合条件(可能,跳转到 flush loop 中完成):如果 flush loop 页没有什么事情可以做了,InnoDB 存储引擎会切换到 suspend loop,将 Master Thread 挂起。

TODO: 未完待续...

Reference

参考资料 Master Thread工作方式 参考资料 MySQL后台线程整理总结 参考资料 玩转MySQL之七 InnoDB存储引擎架构简介